module debug_CPU(
    input clk,
    input rst_n,
    
    input  [31:0] irom_inst,
    output [31:0] irom_pc,
    
    input  [31:0] dram_rd,
    output [31:0] dram_adr,
    output [31:0] dram_wdin,
    output dram_we,
    
    output        debug_wb_have_inst,
    output [31:0] debug_wb_pc,
    output        debug_wb_ena,
    output [4:0]  debug_wb_reg,
    output [31:0] debug_wb_value
    );
    
    wire npc_op_EX;
    wire wb_pc_sel_ID;
    wire wb_pc_sel_EX;
    wire rD1_en_ID, rD2_en_ID;
    wire beq_EX, blt_EX, bltu_EX;
    wire stall_PC, stall_IF_ID;
    wire forward_rD1, forward_rD2;
    wire flush_IF_ID, flush_ID_EX;
    wire alub_sel_ID, alub_sel_EX;
    wire dram_we_ID, dram_we_EX, dram_we_MEM;
    wire rf_we_ID, rf_we_EX, rf_we_MEM, rf_we_WB;
    
    wire [1:0] jump_ID, jump_EX;
    wire [1:0] offset_EX,offset_MEM;

    wire [2:0] sext_op_ID, sext_op_EX;
    wire [2:0] branch_ID, branch_EX;
    wire [2:0] wd_sel_ID, wd_sel_EX, wd_sel_MEM;
    wire [2:0] store_sel_ID,store_sel_EX,store_sel_MEM;

    wire [3:0] alu_op_ID, alu_op_EX;
    wire [3:0] load_sel_ID,load_sel_EX,load_sel_MEM;
    
    wire [4:0] wR_ID, wR_EX, wR_MEM, wR_WB;
    
    wire [31:0] pcimm_ID, pcimm_EX;
    wire [31:0] npc, npc_goto_EX;
    wire [31:0] pc_IF, pc_ID, pc_EX, pc_MEM, pc_WB;;
    wire [31:0] pc4_IF, pc4_ID;
    wire [31:0] inst_IF, inst_ID;
    wire [31:0] imm_ID, imm_EX;
    wire [31:0] rD1_ID, rD1_EX;
    wire [31:0] rD2_ID, rD2_EX, rD2_MEM;
    wire [31:0] aluc_EX, aluc_MEM;
    wire [31:0] dram_rd_MEM;
    wire [31:0] rD1_f, rD2_f;
    wire [31:0] wD_EX, pc4_EX, wD_MEM, wD_MEM_tmp, wD_WB;
    

//////////////////////////////////////////////////////////////////////////////////
// hazard handling
////////////////////////////////////////////////////////////////////////////////// 
    hazard_Control U_hazard_Control(
        .clk            (clk),
        .rst_n          (rst_n),
        .wd_sel         (wd_sel_EX),
        .rD1_en_ID      (rD1_en_ID),
        .rD2_en_ID      (rD2_en_ID),
        .rf_we_EX       (rf_we_EX),
        .rf_we_MEM      (rf_we_MEM),
        .rf_we_WB       (rf_we_WB),
        .rR1_ID         (inst_ID[19:15]),
        .rR2_ID         (inst_ID[24:20]),
        .wR_EX          (wR_EX),
        .wR_MEM         (wR_MEM),
        .wR_WB          (wR_WB),
        .wD_EX          (wD_EX),
        .wD_MEM         (wD_MEM),
        .wD_WB          (wD_WB),
        .npc_op         (npc_op_EX),
        
        .stall_PC       (stall_PC),
        .stall_IF_ID    (stall_IF_ID),
        .flush_IF_ID    (flush_IF_ID),
        .flush_ID_EX    (flush_ID_EX),
        .rD1_f          (rD1_f),
        .rD2_f          (rD2_f),
        .forward_rD1    (forward_rD1),
        .forward_rD2    (forward_rD2)
    );
//////////////////////////////////////////////////////////////////////////////////
// hazard handling
////////////////////////////////////////////////////////////////////////////////// 






//////////////////////////////////////////////////////////////////////////////////
// IF
//////////////////////////////////////////////////////////////////////////////////    
    PC U_PC(
        .clk        (clk),
        .rst_n      (rst_n),
        .stall      (stall_PC),
        .npc        (npc),
        .pc         (pc_IF)
    );

    NPC U_NPC(
        .pc         (pc_IF),
        .npc_goto   (npc_goto_EX),
        .npc_op     (npc_op_EX),
        .npc        (npc)
    );

    assign pc4_IF = pc_IF + 32'd4;

    assign inst_IF = irom_inst;
    assign irom_pc = pc_IF;
//////////////////////////////////////////////////////////////////////////////////
// IF
//////////////////////////////////////////////////////////////////////////////////


    REG_IF_ID U_REG_IF_ID(
        .clk        (clk),
        .rst_n      (rst_n),
        .stall      (stall_IF_ID),
        .flush      (flush_IF_ID),
        .pc_in       (pc_IF),
        .inst_in     (inst_IF),

        .pc_out       (pc_ID),
        .inst_out     (inst_ID)
    );



//////////////////////////////////////////////////////////////////////////////////
// ID
//////////////////////////////////////////////////////////////////////////////////
    assign wR_ID = inst_ID[11:7];
    assign pcimm_ID = pc_ID + imm_ID;
    assign pc4_ID = pc_ID + 32'd4;

    SEXT U_SEXT(
        .din        (inst_ID[31:7]),
        .sext_op    (sext_op_ID),
        .ext        (imm_ID)
    );

    RF U_RF(
        .clk        (clk),
        .rst_n      (rst_n),
        .rR1        (inst_ID[19:15]),
        .rR2        (inst_ID[24:20]),
        .wR         (wR_WB),
        .wD         (wD_WB),
        .rf_we      (rf_we_WB),
        .rD1        (rD1_ID),
        .rD2        (rD2_ID)
    );
    
    Control_ID U_Control_ID(
        .funct7     (inst_ID[31:25]),
        .funct3     (inst_ID[14:12]),
        .opcode     (inst_ID[6:0]),
        .wd_sel     (wd_sel_ID),
        .alu_op     (alu_op_ID),
        .asel       (alub_sel_ID),
        .rf_we      (rf_we_ID),
        .dram_we    (dram_we_ID),
        .sext_op    (sext_op_ID),
        .branch     (branch_ID),
        .jump       (jump_ID),
        .rD1_en     (rD1_en_ID),
        .rD2_en     (rD2_en_ID),
        .wb_pc_sel  (wb_pc_sel_ID),
        .load_sel   (load_sel_ID),
        .store_sel  (store_sel_ID),
        .debug_have_inst    (debug_have_inst_ID)
    );

//////////////////////////////////////////////////////////////////////////////////
// ID
//////////////////////////////////////////////////////////////////////////////////



    REG_ID_EX U_REG_ID_EX(
        .clk        (clk),
        .rst_n      (rst_n),
        .flush      (flush_ID_EX),
        .forward_rD1     (forward_rD1),
        .forward_rD2     (forward_rD2),
        .rf_we_in    (rf_we_ID),
        .dram_we_in  (dram_we_ID),
        .asel_in    (alub_sel_ID),
        .wd_sel_in   (wd_sel_ID),
        .jump_in     (jump_ID),
        .branch_in   (branch_ID),
        .alu_op_in   (alu_op_ID),
        .imm_in      (imm_ID),
        .wR_in       (wR_ID),
        .rD1_f       (rD1_f),
        .rD2_f       (rD2_f),
        .pc_in (pc_ID),
        .rD1_in      (rD1_ID),
        .rD2_in      (rD2_ID),
        .wb_pc_sel_in (wb_pc_sel_ID),
        .load_sel_in   (load_sel_ID),
        .store_sel_in  (store_sel_ID),
        .debug_have_inst_in  (debug_have_inst_ID), 
        .wR_out       (wR_EX),
        .rD1_out      (rD1_EX),
        .rD2_out      (rD2_EX),
        .imm_out      (imm_EX),
        .jump_out     (jump_EX),
        .rf_we_out    (rf_we_EX),
        .wd_sel_out   (wd_sel_EX),
        .alu_op_out   (alu_op_EX),
        .branch_out   (branch_EX),
        .dram_we_out  (dram_we_EX),
        .asel_out     (asel_EX),
        .wb_pc_sel_out (wb_pc_sel_EX),
        .load_sel_out   (load_sel_EX),
        .store_sel_out  (store_sel_EX),
        .pc_out (pc_EX),
        .debug_have_inst_out  (debug_have_inst_EX)
    );


//////////////////////////////////////////////////////////////////////////////////
// EX
//////////////////////////////////////////////////////////////////////////////////
    assign pcimm_EX = pc_EX + imm_EX;
    assign pc4_EX = pc_EX + 32'd4;

    Control_EX U_Control_EX(
        .branch     (branch_EX),
        .jump       (jump_EX),
        .beq        (beq_EX),
        .blt        (blt_EX),
        .bltu       (bltu_EX), 
        .pcimm      (pcimm_EX),
        .aluc       (aluc_EX),
        .wd_sel     (wd_sel_EX),
        .wb_pc_sel  (wb_pc_sel_EX),
        .pc4         (pc4_EX),
        .imm         (imm_EX),
        .wD          (wD_EX),
        .npc_op     (npc_op_EX),
        .npc_goto   (npc_goto_EX)
    );

    ALU U_ALU(
        .rf_rD1      (rD1_EX),
        .rf_rD2      (rD2_EX),
        .sext_ext    (imm_EX),
        .C           (aluc_EX),
        .beq         (beq_EX),
        .blt         (blt_EX),
        .bltu        (bltu_EX),
        .alu_op      (alu_op_EX),
        .offset      (offset_EX),
        .asel        (asel_EX)
    );
/////////////////////////////////////////////s/////////////////////////////////////
// EX
//////////////////////////////////////////////////////////////////////////////////



    REG_EX_MEM U_REG_EX_MEM(
        .clk        (clk),
        .rst_n      (rst_n),
        
        .wd_sel_in   (wd_sel_EX),
        .rf_we_in    (rf_we_EX),
        .dram_we_in  (dram_we_EX),
        .wR_in       (wR_EX),
        .wD_in       (wD_EX),
        .aluc_in     (aluc_EX),
        .rD2_in      (rD2_EX),
        .load_sel_in   (load_sel_EX),
        .store_sel_in  (store_sel_EX),
        .offset_in     (offset_EX),
        
        .wd_sel_out   (wd_sel_MEM),
        .rf_we_out    (rf_we_MEM),
        .dram_we_out  (dram_we_MEM),
        .wR_out       (wR_MEM),
        .wD_out       (wD_MEM_tmp),
        .aluc_out     (aluc_MEM),
        .rD2_out      (rD2_MEM),
        .load_sel_out   (load_sel_MEM),
        .store_sel_out  (store_sel_MEM),
        .offset_out     (offset_MEM),

        .pc_in         (pc_EX),
        .pc_out         (pc_MEM),
        .debug_have_inst_in  (debug_have_inst_EX),
        .debug_have_inst_out  (debug_have_inst_MEM)
    );



//////////////////////////////////////////////////////////////////////////////////
// MEM
//////////////////////////////////////////////////////////////////////////////////
    assign dram_adr = aluc_MEM;
    assign dram_we = dram_we_MEM;
    assign dram_rd_MEM = dram_rd;

    Control_MEM U_Control_MEM(
        .wd_sel     (wd_sel_MEM),
        .rD2        (rD2_MEM),
        .dram_rd    (dram_rd_MEM),
        .load_sel   (load_sel_MEM),
        .store_sel  (store_sel_MEM),
        .offset     (offset_MEM),
        .wD         (wD_MEM_tmp),
        .wD_out     (wD_MEM),
        .wdin       (dram_wdin)
    );
//////////////////////////////////////////////////////////////////////////////////
// MEM
//////////////////////////////////////////////////////////////////////////////////



    REG_MEM_WB U_REG_MEM_WB(
        .clk        (clk),
        .rst_n      (rst_n),
        
        .rf_we_in    (rf_we_MEM),
        .wR_in       (wR_MEM),
        .wD_in       (wD_MEM),
        
        .rf_we_out    (rf_we_WB),
        .wR_out       (wR_WB),
        .wD_out       (wD_WB),
        
        .pc_in         (pc_MEM),
        .pc_out        (pc_WB),
        .debug_have_inst_in  (debug_have_inst_MEM),
        .debug_have_inst_out  (debug_have_inst_WB)
    );



//////////////////////////////////////////////////////////////////////////////////
// debug signal
////////////////////////////////////////////////////////////////////////////////// 
    wire   debug_have_inst_ID; 
    wire   debug_have_inst_EX;
    wire   debug_have_inst_MEM; 
    wire   debug_have_inst_WB;
    assign debug_wb_ena = rf_we_WB;
    assign debug_wb_reg = wR_WB;
    assign debug_wb_value = wD_WB;
    assign debug_wb_pc = pc_WB;
    assign debug_wb_have_inst = debug_have_inst_WB;
//////////////////////////////////////////////////////////////////////////////////
// debug signal
////////////////////////////////////////////////////////////////////////////////// 


endmodule
